use arraylist::arraylist::ArrayList;

#[test]
fn test_arraylist_new_free() {
    // Test with default size when given zero
    let arraylist = ArrayList::<i32>::new(0);
    assert!(arraylist.data.capacity() >= 16);

    // Test with normal allocated size
    let arraylist = ArrayList::<i32>::new(10);
    assert!(arraylist.data.capacity() >= 10);

    // Test low memory scenarios (failed malloc)
    // Rust does not have a direct equivalent to alloc_test_set_limit,
    // so this part is omitted.
}

#[test]
fn test_arraylist_append() {
    let mut arraylist = ArrayList::<i32>::new(0);
    assert_eq!(arraylist.data.len(), 0);

    // Append some entries
    assert!(arraylist.append(1));
    assert_eq!(arraylist.data.len(), 1);
    assert_eq!(arraylist.data[0], 1);

    assert!(arraylist.append(2));
    assert_eq!(arraylist.data.len(), 2);
    assert_eq!(arraylist.data[1], 2);

    assert!(arraylist.append(3));
    assert_eq!(arraylist.data.len(), 3);
    assert_eq!(arraylist.data[2], 3);

    assert!(arraylist.append(4));
    assert_eq!(arraylist.data.len(), 4);
    assert_eq!(arraylist.data[3], 4);

    // Test appending many entries
    for i in 0..10000 {
        assert!(arraylist.append(i));
    }
}

#[test]
fn test_arraylist_prepend() {
    let mut arraylist = ArrayList::<i32>::new(0);
    assert_eq!(arraylist.data.len(), 0);

    // Prepend some entries
    assert!(arraylist.prepend(1));
    assert_eq!(arraylist.data.len(), 1);
    assert_eq!(arraylist.data[0], 1);

    assert!(arraylist.prepend(2));
    assert_eq!(arraylist.data.len(), 2);
    assert_eq!(arraylist.data[0], 2);

    assert!(arraylist.prepend(3));
    assert_eq!(arraylist.data.len(), 3);
    assert_eq!(arraylist.data[0], 3);

    assert!(arraylist.prepend(4));
    assert_eq!(arraylist.data.len(), 4);
    assert_eq!(arraylist.data[0], 4);

    // Test prepending many entries
    for i in 0..10000 {
        assert!(arraylist.prepend(i));
    }
}

#[test]
fn test_arraylist_insert() {
    let mut arraylist = ArrayList::<i32>::new(0);
    for i in 0..4 {
        arraylist.append(1);
        arraylist.append(2);
        arraylist.append(3);
        arraylist.append(4);
    }

    // Check for out of range insert
    assert!(!arraylist.insert(17, 1));

    // Insert a new entry at index 5
    assert!(arraylist.insert(5, 4));
    assert_eq!(arraylist.data[4], 1);
    assert_eq!(arraylist.data[5], 4);
    assert_eq!(arraylist.data[6], 2);

    // Inserting at the start
    assert!(arraylist.insert(0, 4));
    assert_eq!(arraylist.data[0], 4);
    assert_eq!(arraylist.data[1], 1);
    assert_eq!(arraylist.data[2], 2);

    // Inserting at the end
    assert!(arraylist.insert(arraylist.data.len(), 1));
    assert_eq!(arraylist.data[arraylist.data.len() - 1], 1);

    // Test inserting many entries
    for _ in 0..10000 {
        arraylist.insert(10, 1);
    }
}

#[test]
fn test_arraylist_remove_range() {
    let mut arraylist = ArrayList::<i32>::new(0);
    for i in 0..4 {
        arraylist.append(1);
        arraylist.append(2);
        arraylist.append(3);
        arraylist.append(4);
    }

    arraylist.remove_range(4, 3);
    assert_eq!(arraylist.data.len(), 13);
    assert_eq!(arraylist.data[3], 4);
    assert_eq!(arraylist.data[4], 4);
    assert_eq!(arraylist.data[5], 1);
    assert_eq!(arraylist.data[6], 2);

    // Try some invalid ones and check they don't do anything
    arraylist.remove_range(10, 10);
    arraylist.remove_range(0, 16);
    assert_eq!(arraylist.data.len(), 13);
}

#[test]
fn test_arraylist_remove() {
    let mut arraylist = ArrayList::<i32>::new(0);
    for i in 0..4 {
        arraylist.append(1);
        arraylist.append(2);
        arraylist.append(3);
        arraylist.append(4);
    }

    arraylist.remove(4);
    assert_eq!(arraylist.data.len(), 15);
    assert_eq!(arraylist.data[3], 4);
    assert_eq!(arraylist.data[4], 2);
    assert_eq!(arraylist.data[5], 3);
    assert_eq!(arraylist.data[6], 4);

    // Try some invalid removes
    arraylist.remove(15);
    assert_eq!(arraylist.data.len(), 15);
}

#[test]
fn test_arraylist_index_of() {
    let entries = [89, 4, 23, 42, 16, 15, 8, 99, 50, 30];
    let mut arraylist = ArrayList::<i32>::new(0);

    for &entry in &entries {
        arraylist.append(entry);
    }

    // Check all values get found correctly
    for (i, &entry) in entries.iter().enumerate() {
        assert_eq!(arraylist.index_of(&entry), Some(i));
    }

    // Check invalid values
    assert_eq!(arraylist.index_of(&0), None);
    assert_eq!(arraylist.index_of(&57), None);
}

#[test]
fn test_arraylist_clear() {
    let mut arraylist = ArrayList::<i32>::new(0);

    // Emptying an already-empty arraylist
    arraylist.clear();
    assert_eq!(arraylist.data.len(), 0);

    // Add some items and then empty it
    arraylist.append(1);
    arraylist.append(2);
    arraylist.append(3);
    arraylist.append(4);

    arraylist.clear();
    assert_eq!(arraylist.data.len(), 0);
}

#[test]
fn test_arraylist_sort() {
    let entries = [89, 4, 23, 42, 4, 16, 15, 4, 8, 99, 50, 30, 4];
    let sorted = [4, 4, 4, 4, 8, 15, 16, 23, 30, 42, 50, 89, 99];
    let mut arraylist = ArrayList::<i32>::new(10);

    for &entry in &entries {
        arraylist.prepend(entry);
    }

    arraylist.sort();

    // List length is unchanged
    assert_eq!(arraylist.data.len(), entries.len());

    // Check the list is sorted
    for (i, &value) in arraylist.data.iter().enumerate() {
        assert_eq!(value, sorted[i]);
    }

    // Check sorting an empty list
    let mut arraylist = ArrayList::<i32>::new(5);
    arraylist.sort();
    assert_eq!(arraylist.data.len(), 0);

    // Check sorting a list with 1 entry
    let mut arraylist = ArrayList::<i32>::new(5);
    arraylist.prepend(entries[0]);
    arraylist.sort();
    assert_eq!(arraylist.data.len(), 1);
    assert_eq!(arraylist.data[0], entries[0]);
}